﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Net8.Project.Common;
using Net8.Project.Common.Extensions;
using System.IdentityModel.Tokens.Jwt;
using System.Text;

namespace Net8.Project.Extension.Authorization
{
    /// <summary>
    /// JWT权限 认证服务
    /// </summary>
    public static class Authentication_JWTSetup
    {
        #region 获取Jwt认证密钥
        private static string Audience_Secret = AppSettings.App(new string[] { "Startup", "Jwt", "Secret" });
        private static string Audience_Secret_File = AppSettings.App(new string[] { "Startup", "Jwt", "SecretFile" });
        public static string Audience_Secret_String => InitAudience_Secret();
        private static string InitAudience_Secret()
        {
            var securityString = DifDBConnOfSecurity(Audience_Secret_File);
            if (!string.IsNullOrEmpty(Audience_Secret_File) && !string.IsNullOrEmpty(securityString))
            {
                return securityString;
            }
            else
            {
                return Audience_Secret;
            }

        }
        private static string DifDBConnOfSecurity(params string[] conn)
        {
            foreach (var item in conn)
            {
                try
                {
                    if (File.Exists(item))
                    {
                        return File.ReadAllText(item).Trim();
                    }
                }
                catch (System.Exception) { }
            }

            return "";
        } 
        #endregion

        public static void AddAuthentication_JWTSetup(this IServiceCollection services) 
        {
            ArgumentNullException.ThrowIfNull(services);

            var symmetricKeyAsBase64 = Authentication_JWTSetup.Audience_Secret_String;
            var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            var Issuer = AppSettings.App(["Startup", "Jwt", "Issuer"]);
            var Audience = AppSettings.App(["Startup", "Jwt", "Audience"]);
            var ExpirationHours = AppSettings.App(["Startup", "Jwt", "ExpirationHours"]);
            _ = int.TryParse(ExpirationHours, out int Hours);

            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);

            // 令牌验证参数
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,

                ValidIssuer = Issuer,//发行人
                ValidAudience = Audience,//订阅人
                IssuerSigningKey = signingKey,
                ClockSkew = TimeSpan.FromSeconds(30),
                RequireExpirationTime = true,
            };

            // 开启Bearer认证
            services.AddAuthentication(o =>
            {
                o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
             // 添加JwtBearer服务
             .AddJwtBearer(o =>
             {
                 o.TokenValidationParameters = tokenValidationParameters;
                 o.Events = new JwtBearerEvents
                 {
                     OnMessageReceived = context =>
                     {
                         var accessToken = context.Request.Query["access_token"];

                         // If the request is for our hub...
                         var path = context.HttpContext.Request.Path;
                         if (!string.IsNullOrEmpty(accessToken) &&
                             (path.StartsWithSegments("/api2/chathub")))
                         {
                             // Read the token out of the query string
                             context.Token = accessToken;
                         }
                         return Task.CompletedTask;
                     },
                     OnChallenge = context =>
                     {
                         context.Response.Headers["Token-Error"] = context.ErrorDescription;
                         return Task.CompletedTask;
                     },
                     OnAuthenticationFailed = context =>
                     {
                         var jwtHandler = new JwtSecurityTokenHandler();
                         var token = context.Request.Headers.Authorization.ObjToString().Replace("Bearer ", "");

                         if (token.IsNotEmptyOrNull() && jwtHandler.CanReadToken(token))
                         {
                             var jwtToken = jwtHandler.ReadJwtToken(token);

                             if (jwtToken.Issuer != Issuer)
                             {
                                 context.Response.Headers["Token-Error-Iss"] = "issuer is wrong!";
                             }

                             if (jwtToken.Audiences.FirstOrDefault() != Audience)
                             {
                                 context.Response.Headers["Token-Error-Aud"] = "Audience is wrong!";
                             }
                         }

                         // 如果过期，则把<是否过期>添加到，返回头信息中
                         if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                         {
                             context.Response.Headers["Token-Expired"] = "true";
                         }
                         return Task.CompletedTask;
                     }
                 };
             });

            // 添加授权策略
            services.AddAuthorizationBuilder()
                                     // 添加授权策略
                                     .AddPolicy("Client", policy => policy.RequireClaim("iss", Issuer).Build())
                                     .AddPolicy("SuperAdmin", policy => policy.RequireRole("SuperAdmin").Build())
                                     .AddPolicy("SystemOrAdmin", policy => policy.RequireRole("SuperAdmin", "System"))
                                     .AddPolicy("Permission", policy => policy.Requirements.Add(new PermissionRequirement()));

            // 注入自定义权限处理器
            services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();
            services.AddSingleton(new PermissionRequirement(Issuer, Audience, signingCredentials, TimeSpan.FromHours(Hours)));
        }
    }
}
